

#include <stdio.h>
#include <stdlib.h>
#include "mymonitor.h"

#define NUM_THREADS	4

/* Keept track of the thread numbers */
int numThrd = 0;
int awakeThreads = NUM_THREADS;

/* Monitor variable used to guarantee and test mutual exclusion */
monitor_t testMonitor;

/**
 * Tests the monitor functionality by having every even thread wait, and every odd thread call broadcast
 *
 *@param num  Integer variable representing the number of the thread running
 */
void *testMonitorFunctions(void *num){
	/* Save the thread number in inputNum, so that the pointer doesn't change during execution */
	int *inNum = (int *) num;
	int inputNum = *inNum;
	numThrd++;
	
	/* Enter the monitor, blocking may occur here if the thread is forced to back off */
	int flag = monitor_enter(&testMonitor);
	//printf("Thread %d: Entering the monitor!!!!\n", inputNum);
	
	/* Wait if you are an even thread, and there is some thread to awaken you */
	if(((inputNum % 2) == 0) && (awakeThreads > 1)){
		awakeThreads--;
		monitor_wait(&testMonitor);
		awakeThreads++;
	} else if (((inputNum % 2) == 0) && (awakeThreads <= 1)){
		printf("Thread %d: Cannot wait, because there is no thread to awaken me!!!!\n", inputNum);
	}
	
	/* Signal blocked thread to wake up */
	monitor_signal(&testMonitor);
	
	/* Broadcast if you are the last thread (inputNum = NUM_THREADS - 1) */
	if(inputNum == NUM_THREADS - 1){
		monitor_broadcast(&testMonitor);
	}
	
	/*printf("Thread %d: Exiting the monitor!!!!\n", inputNum);*/
	monitor_exit(&testMonitor);

	/* Keep track of how many threads are awake, if none are awake, then wait should not be called */
	awakeThreads--;
	mythread_exit(NULL);
}

/**
 * Main function, sets up the threads
 *
 *@param argc  Number of arguments
 *@param argv  Arguments to the function
 *@return  Integer indicating proper or improper exit
 */
int main (int argc, char *argv[]){
    mythread_t thread[NUM_THREADS];
    int tNum = 0;
    int errorCheck;
	
	/* Initialize the monitor variable that will be used for testing */
	monitor_init(&testMonitor);
	
	mythread_setconcurrency(2);

    /* Create the threads for monitor testing, even threads will wait, and odd threads will signal */
    for(tNum = 0; tNum < NUM_THREADS; tNum++)
    {
		errorCheck = mythread_create(&thread[tNum], NULL, testMonitorFunctions, (void *) &numThrd);
		if (errorCheck)
		{
			printf("ERROR; return code from mythread_create() for thread %d is %d\n", tNum, errorCheck);
			exit(-1);
        }
    }
   
    /* Join the threads */
    void *status;
    for(tNum = 0; tNum < NUM_THREADS; tNum++){
		errorCheck = mythread_join(thread[tNum], &status);
		if (errorCheck){
			printf("ERROR; return code from mythread_join() for thread %d is %d\n", tNum, errorCheck);
			exit(-1);
        }
    }

	/* Destroy the monitor */
	monitor_destroy(&testMonitor);
	
	printf("TEST: All threads complete successfully, wait and signal functionality PASS!\n");
   
    /* printf("Main thread exiting via mythread_exit.\n"); */
	mythread_exit(NULL);

	return 0;
}
